package xdu.lz.stage1_basic.chapter3_threadCommunicate;
//需求：模拟一个消费者与生产者的关系

/*
问题1：出现了线程安全问题，价格错乱了。线程安全问题具体原因见java.doc的图.
	 解决：锁
	 
问题2：要达到以下效果：生产者先生产一个P，然后等待消费者消费完P后再生产。
	
wait():  等待   如果线程执行了wait方法，那么该线程会进入等待的状态，等待状态下的线程必须要被其他线程调用notify方法才能唤醒。
notify()： 唤醒    唤醒线程池等待线程其中的一个。
notifyAll() : 唤醒线程池所有等待 线程。

wait与notify方法要注意的事项：
	1. wait方法与notify方法是属于Object对象 的。因为wait和notify的对象是锁，锁又是任意的对象，所以是OBJ
	2. wait方法与notify方法必须要在同步代码块或者是同步函数中才能 使用。 因为在这两者下才有锁的概念，而这两个函数是有锁对象调用的
	3. wait方法与notify方法必需要由锁对象调用。
	

*/

class Product{
	String name;
	double price;
	boolean flag = false ;//产品生产标识，默认是没有生产完
}

class Producer extends Thread{
	Product p;
	public Producer(Product p) {  //保证 生产者和消费者的产品为同一个产品对象
		this.p = p ;
	}
	@Override
	public void run() {
		int i = 0;
		while(true){
			synchronized (p) {
				if(p.flag == false){ //如果没有生产完，则生产
					if(i%2 == 0){
						p.name = "苹果";
						try {
							Thread.sleep(10);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						p.price = 6.5;
					}else{
						p.name = "香蕉";
						p.price = 2.0 ;
					}
					System.out.println("生产者生产了："+p.name+"   价格是："+p.price);
					i ++ ;
					p.flag = true ; //生产完毕
					p.notifyAll(); //唤醒消费者消费
				}else{
					try {
						p.wait();  //生产完毕，等待消费者消费
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
			}
			
		}
	}
}

class Customer extends Thread{
	Product p;
	public Customer(Product p) {//保证 生产者和消费者的产品为同一个产品对象
		this.p = p ;
	}
	@Override
	public void run() {
		while(true){
			synchronized (p) {
				
				if(p.flag == true){ //如果生产完毕则消费
					System.out.println("消费了"+p.name+"价格是："+p.price);	
					p.flag = false ; //消费完毕
					p.notifyAll();//唤醒生产者生产
				}else{
					//产品还没有生产,应该 等待生产者先生产。
					try {
						p.wait();
					} catch (InterruptedException e) {

						e.printStackTrace();
					} //消费者也等待了...
				}
			}
			
		
		}
	}
}
public class ProducerConsumer {
	public static void main(String[] args) {
		
		Product  p = new Product();
		
		Producer producer = new Producer(p);   //这样保证消费者和生产者的产品P是同一个P
		Customer customer = new Customer(p);
		
		producer.start();
		customer.start();
		
		
		Producer producer1 = new Producer(p);   //这样保证消费者和生产者的产品P是同一个P
		Customer customer1 = new Customer(p);
		Customer customer3 = new Customer(p);
		Customer customer4 = new Customer(p);
		Customer customer5 = new Customer(p);
		producer1.start();
		customer1.start();
		customer3.start();
		customer4.start();
		customer5.start();
	}
}


